#ifndef GRAFO_H
#define GRAFO_H

#include <iostream>
#include <fstream>
#include <cassert>

#include "nodo.h"
#include "arco.h"
#include <glib.h>

using namespace std;

class Grafo : public Elemento_ui
{
    public:
      /** Default constructor */
      Grafo();
      /** Default destructor */
      virtual ~Grafo();
      
      /** Inizializza il grafo.
       * Il grafo corrente viene eliminato, portando il grafo ad una situazione vuota.
       * L'indice corrente viene re-inizializzato a 0.              
       */
      void inizializza();
      
      /** Trova il nodo posto alle coordinate indicate.
       * @param[in] x Coordinata X del punto
       * @param[in] y Coordinata Y del punto
       * @return Elemento grafico alle coordinate selezionate, NULL se nessun elemento giace al loro interno.
       */
      Elemento_ui* find_by_xy(gdouble x, gdouble y);
      
      /** Seleziona il nodo
       * @param[in] nodo Nodo da selezionare
       */
      void seleziona(Nodo *nodo);
      
      /** Seleziona il nodo presente alle coordinate specificate.
       * Se le coordinate specificate corrispondono ad un nodo esistente, questo diviene la selezione corrente, altrimenti la selezione rimane inalterata.
       * @param[in] x Coordinata X
       * @param[in] y Coordinata Y
       */
      void seleziona(gdouble x, gdouble y);
      
      /** Cerca e restituisce il nodo selezionato.
       * @return Il nodo correntemente in stato selezionato.
       */
      Nodo* get_selezionato();
      
      /** Aggiunge un nodo al grafo
       * @param[in] x coordinata X alla quale crearlo
       * @param[in] y coordinata Y alla quale crearlo
       * @return Il nodo appena creato e aggiunto alla lista
       */
       Nodo* aggiungi_nodo(gdouble x, gdouble y);
      
       
      /** Aggiunge un arco tra due nodi esistenti.
       * L'arco di origine è sempre il nodo correntemente selezionato, mentre il nodo di destinazione può variare.
       * @param[in] destinazione Nodo di destinazione
       */
      void aggiungi_arco(Nodo* destinazione);
          
      /** Segnala all'oggetto corrente la pressione del pulsante sinistro del mouse.
       * @param[in] x Coordinata X in cui è stato premuto il pulsante del mouse
       * @param[in] y Coordinata Y in cui è stato premuto il pulsante del mouse
       */
      void lmb_premuto(gdouble x, gdouble y);
      
      /** segnala all'oggetto il rilascio del pulsante sinistro del mouse.
       */
      void lmb_rilasciato();        
      
      /** Calcola se siamo durante un'operazione di trascinamento del mouse.
       * @return TRUE se si sta trascinando il mouse con il pulsante sinistro premuto, FALSE altrimenti
       */
      gboolean is_dragging();     
      /** Restituisce la lista dei nodi
      * @return Lista di ::Nodo 
      */
      GSList* get_nodi() { return nodi; }
      
      /** Restituisce la lista di adiacenze
      * @return Lista di ::Arco
      */
      GSList* get_archi() {return lista_adiacenze; }
      
      /** Prepara i nodi all'esecuzione degli algoritmi di Djkstra o BellmanFord.
      * Imposta tutte le distanze a infinito e elimina tutti i percorsi.
      */
      void prepara();
      
      /** Restituisce il numero di nodi presenti nel grafo
      * @return Numero di nodi presenti nel grafo
      */
      guint get_numero_nodi();
      
      /** Salva il grafo su un file.
      * @param[in] file Nome del file sul quale scrivere il contenuto del grafo.
      */
      void salva(const char* file);
      
      /** Carica il grafo da un file.
      * L'eventuale contenuto corrente del grafo viene scartato.
      * @param[in] file Nome del file dal quale caricare il contenuto del grafo.
      */
      void carica(const char* file);
      
      virtual void draw(cairo_t* &cr);
      virtual void update(gdouble x, gdouble y);

    protected:
    private:
        Nodo* selezionato; //!< Memorizza il nodo selezionato per aggiornarlo rapidamente
        Arco* arco_selezionato; //!< Memorizza l'arco selezionato
        guint indice_corrente; //!< Memorizza l'ultimo indice assegnato

    	GSList* nodi; //!< Lista dei nodi appartenenti al grafo
        GSList* lista_adiacenze; //!< Implementiamo gli archi con una lista di adiacenze

        Nodo* find_by_index(guint indice); //!< Restituisce il nodo con l'indice indicato
        void deseleziona_nodi(); //!< Deseleziona tutti i nodi
        void carica_elemento(istream &ifs); //!< Carica un singolo elemento da uno stream
        gboolean is_lmb_pressed; //!< Indica se il pulsante sinistro del mouse è attualmente premuto
        gdouble mouse_origin_x; //!< Coordinata X del punto in cui è stato premuto il pulsante sinistro del mouse
        gdouble mouse_origin_y; //!< coordinata Y del punto in cui è stato premuto il pulsante sinistro del mouse        
};

#endif // GRAFO_H
